#pragma once

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <functional>
namespace Server
{
    using namespace std;

    //默认的点分十进制IP
    static const string defaultIP="0.0.0.0";
    static const int gnum=1024;//缓冲区大小
    
    enum{USAGE_ERR=1,SOCKET_ERR,BIND_ERR};//退出码

    typedef function<void(string,uint16_t,string)> func_t;

    class udpServer
    {
    public:
        udpServer(const func_t& callback,const uint16_t &port,const string& ip=defaultIP)
            :callback_(callback),port_(port),ip_(ip),sockfd_(-1)
        {}
        void initServer()
        {
            //****创建UDP网络通信端口****
            sockfd_=socket(AF_INET,SOCK_DGRAM,0);
            if(sockfd_==-1)
            {
                cerr << "socket error: " << errno << " : " << strerror(errno) << endl;
                exit(SOCKET_ERR);
            }
            cout << "socket success: " << " : " << sockfd_ << endl;

            //******绑定port，ip****
            //服务器要明确绑定port，不能随意改变，需要程序员显示指明
            struct sockaddr_in local;
            bzero(&local,sizeof(local));//对结构体数据清0

            //填充结构体

            local.sin_family=AF_INET;
            //大小端转换，给别人发消息，端口号和IP地址也要发给对方
            local.sin_port=htons(port_);//端口号转网络

            //1.string->uint32_t  2.htonl()---->两个工作统一交给inet_addr
            local.sin_addr.s_addr=inet_addr(ip_.c_str());//ip转网络
            // local.sin_addr.s_addr=htonl(INADDR_ANY);//任意地址绑定，服务器真正写法

            int n=bind(sockfd_,(struct sockaddr*)&local,sizeof(local));
            if(n==-1)
            {
                cerr << "bind error: " << errno << " : " << strerror(errno) << endl;
                exit(BIND_ERR);
            }
        }
        void start()
        {
            char buffer[gnum];
            for(;;)
            {
                //****读取数据****

                //服务器的本质就是一个死循环
                struct sockaddr_in peer;//做输入输出型参数
                socklen_t len=sizeof(peer);//必填
                //数据获取：IP地址+端口号+数据
                ssize_t s= recvfrom(sockfd_,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);
                if(s>0)
                {
                    buffer[s]=0;
                    //1.网络->主机 2.uint32_t -> 点分十进制
                    //string clientip=peer.sin_addr.s_addr;需要转两步，不方便 
                    string clientip=inet_ntoa(peer.sin_addr);
                    uint16_t clientport=ntohs(peer.sin_port);
                    string message=buffer;

                    cout<<clientip<<"["<<clientport<<"]#"<<message<<endl;
                    //利用回调方法处理数据，实现解耦
                    callback_(clientip,clientport,message);
                }
            }
        }

        ~udpServer(){}
    private:
        uint16_t port_;//端口号
        string ip_;//IP地址
        int sockfd_;//文件描述符
        func_t callback_;//回调
    };
}